package com.gmall.gateway.filter;

import cn.hutool.core.date.DateUtil;
import com.gmall.gateway.enums.ErrorCodeType;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Summary;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @author HL.Wu
 * @date 2020/7/2 17:35
 * Copyright ©https://blog.csdn.net/qq_31150503 Copyright@2009-2020 AII Right Reserve
 */

@Component
@Slf4j
public class MonitorHandlerFilter implements GlobalFilter, Ordered {

    private static final String START_TIME = "startTime";
    private static final String METRIC_NAMESPACE = "gmall_gateway";


    private static final Summary requestCost = Summary.build()
            .namespace(METRIC_NAMESPACE)
            .name("request_cost")
            .help("process request gateway cost")
            .create();

    private static final Counter requestCounter = Counter.build()
            .namespace(METRIC_NAMESPACE)
            .name("request_count_total")
            .help("total request gateway")
            .create();

    private static final Counter requestErrorCounter = Counter.build()
            .namespace(METRIC_NAMESPACE)
            .name("request_error_count_total")
            .help("total request error gateway")
            .create();

    public MonitorHandlerFilter(PrometheusMeterRegistry registry) {
        requestCost.register(registry.getPrometheusRegistry());
        requestCounter.register(registry.getPrometheusRegistry());
        requestErrorCounter.register(registry.getPrometheusRegistry());
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest originalRequest = exchange.getRequest();
        ServerHttpResponse resp = exchange.getResponse();
        URI originalRequestUrl = originalRequest.getURI();
        //只记录http的请求
        String scheme = originalRequestUrl.getScheme();
        if ((!"http".equals(scheme) && !"https".equals(scheme))) {
            return chain.filter(exchange);
        }

        String upgrade = originalRequest.getHeaders().getUpgrade();
        if ("websocket".equalsIgnoreCase(upgrade)) {
            return chain.filter(exchange);
        }
        exchange.getAttributes().put(START_TIME, DateUtil.date().getTime());
        // 请求执行埋点 消息记录时间处理
        return requestCost.time(() -> chain.filter(exchange).then( Mono.fromRunnable(() -> {
            Long startTime = exchange.getAttribute(START_TIME);
            ServerHttpResponse serverHttpResponse = exchange.getResponse();
            recodeErrorRequest(exchange.getRequest().getURI().getPath(),serverHttpResponse.getStatusCode().value(),serverHttpResponse.getStatusCode().getReasonPhrase());
            if (startTime != null) {
                Long executeTime = (DateUtil.date().getTime() - startTime);
                log.info(exchange.getRequest().getURI().getRawPath() + " : {} " , executeTime + "ms");
            }
        })));
    }

    public void recodeErrorRequest(String requestUrl,int responseCode, String message){
        boolean isSuccess = false;
        for (ErrorCodeType httpBizCode: ErrorCodeType.values()) {
            // 非指定错误码均进行异常通知
            if((httpBizCode.getCode() == responseCode) || responseCode == 200){
                isSuccess = true;
                break;
            }
        }
        if(!isSuccess){
            log.error("current request api occur error \n path is  :{} \n error code is : {} \n error message is : {}",requestUrl,responseCode,message);
            // TODO send wechat email
            // Api请求异常
            requestErrorCounter.inc();
        }
    }

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,
     * the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     *
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
